package edu.ccut.saturn.component;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.dom4j.Document;
import org.dom4j.Element;

import edu.ccut.saturn.component.utils.SaturnLogger;
import edu.ccut.saturn.component.utils.logger.DebugLoggerListener;
import edu.ccut.saturn.component.utils.logger.Log4jLoggerListener;
import edu.ccut.saturn.component.xml.Dom4jUtils;

/**
 * Saturn日志管理。定义了日志的5个类别：
 * LOGGER_KEY_BUNDLE_MANAGER、LOGGER_KEY_LOAD_BUNDLE_CONFIG、LOGGER_KEY_COMPONENT_RUNTIME、
 * LOGGER_KEY_LOAD_BUNDLE、LOGGER_KEY_DATABASE_CONFIG
 * 
 * @author LeslieGu
 *
 */
public class SaturnLoggerManager {

	public static final String LOGGER_KEY_BUNDLE_SERVICE = "bundle-service";

//	public static final String LOGGER_KEY_LOAD_BUNDLE_CONFIG = "load-bundle-config";

	public static final String LOGGER_KEY_COMPONENT_RUNTIME = "component-runtime";

	public static final String LOGGER_KEY_LOAD_BUNDLE = "load-bundle";

//	public static final String LOGGER_KEY_DATABASE_CONFIG = "database-config";
	
	public static final String DEFAULT_LEVEL = ILogger.LOGGER_LEVLE_INFO;

	private static final String DEFAULT_LOGGER_CONFIG_FILE = "/logger.config";

	private static final String QUERY_SQL = "SELECT value FROM saturnconfig WHERE type = 'logger' AND symbolicName = ?";

	private static final String DELETE_SQL = "DELETE FROM saturnconfig WHERE type = 'logger' AND symbolicName = ?";
	
	private static final String UPDATE_SQL = "UPDATE saturnconfig SET value = ? WHERE type = 'logger' AND symbolicName = ?";
	
	private static final String INSERT_SQL = "insert into saturnconfig (id,type,symbolicName,value) values (?,?,?,?)";

	private static final String KEY_LEVEL = "level";

	private static final String KEY_ID = "id";

	private static SaturnLoggerManager instance;

	private Map<String, ILogger> loggers;

	private Map<String, String> loggerDefaultLevelMap;
	
	private List<String> noLoggerFullKey;
	
	private List<String> allLevel;

	static {
		addLoggerListener(new Log4jLoggerListener());
		addLoggerListener(new DebugLoggerListener());
		instance = new SaturnLoggerManager();
	}

	public static SaturnLoggerManager getInstance() {
		return instance;
	}
	
	public static void addLoggerListener(ILoggerListener loggerListener) {
		SaturnLogger.addLoggerListener(loggerListener);
	}
	
	public static void removeLoggerListener(ILoggerListener loggerListener) {
		SaturnLogger.removeLoggerListener(loggerListener);
	}
	

	public List<String> getNoLoggerInfoFullKey() {
		return noLoggerFullKey;
	}

	public void setNoLoggerInfoFullKey(List<String> noLoggerInfoFullKey) {
		this.noLoggerFullKey = noLoggerInfoFullKey;
	}

	public ILogger getSaturnLogger(String key) {
		if (loggers.containsKey(key)) {
			return loggers.get(key);
		}

		return null;
	}
	
	public Collection<ILogger> getSaturnLoggers() {
		return this.loggers.values();
	}
	
	public Set<String> getSaturnLoggerKeys() {
		return this.loggers.keySet();
	}
	
	public void setSaturnLogger(String key, String level) {
		ILogger logger = this.getSaturnLogger(key);
		
		if (logger != null) {
			logger.setLevel(level);
			this.setLoggerFromDatabase(key, level);
		}
	}

	public void setSaturnLoggerToDefaultLevel(String key) {
		ILogger logger = this.getSaturnLogger(key);
		
		if (logger != null) {
			logger.setLevel(DEFAULT_LEVEL);
			this.deleteLoggerLevelFromDatabase(key);
		}
	}
	
	public String getLoggerDefaultLevel(String key) {
		return this.loggerDefaultLevelMap.get(key);
	}
	
	public List<String> getAllLevel() {
		return allLevel;
	}

	private SaturnLoggerManager() {
		this.loggerDefaultLevelMap = new LinkedHashMap<String, String>();
		this.loggers = new LinkedHashMap<String, ILogger>();
		
		this.allLevel = new ArrayList<String>();
		this.allLevel.add(ILogger.LOGGER_LEVLE_INFO);
		this.allLevel.add(ILogger.LOGGER_LEVLE_DEBUG);
		this.allLevel.add(ILogger.LOGGER_LEVLE_WARN);
		this.allLevel.add(ILogger.LOGGER_LEVLE_ERROR);
		this.allLevel.add(ILogger.LOGGER_LEVLE_FATAL);
		
		loadLoggerDefaultLevelMap();
		initalLogger();
	}
	
	@SuppressWarnings("unchecked")
	private void loadLoggerDefaultLevelMap() {
		Element root = null;
		InputStream in = null;
		try {
			URL url = SaturnLoggerManager.class.getResource(DEFAULT_LOGGER_CONFIG_FILE);
			in = url.openStream();
			
			Document document = Dom4jUtils.getDocment(in);
			root = document.getRootElement();

			if (root != null) {
				List<Element> list = (List<Element>) root.elements();

				if (list.size() > 0) {
					for (Element element : list) {
						if (element != null) {
							loggerDefaultLevelMap.put(element
									.attributeValue(KEY_ID), element
									.attributeValue(KEY_LEVEL));
						}
					}
				}
			}
		} catch (Exception e) {
			// if load logger.config failed, put default logger INFO
			loggerDefaultLevelMap.put(LOGGER_KEY_BUNDLE_SERVICE,
					DEFAULT_LEVEL);

	/*		loggerDefaultLevelMap.put(LOGGER_KEY_LOAD_BUNDLE_CONFIG,
					DEFAULT_LEVEL);*/

			loggerDefaultLevelMap.put(LOGGER_KEY_COMPONENT_RUNTIME,
					DEFAULT_LEVEL);

			loggerDefaultLevelMap.put(LOGGER_KEY_LOAD_BUNDLE,
					DEFAULT_LEVEL);

		/*	loggerDefaultLevelMap.put(LOGGER_KEY_DATABASE_CONFIG,
					DEFAULT_LEVEL);*/
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private void initalLogger() {
		Set<String> loggerKeys = loggerDefaultLevelMap.keySet();

		if (loggerKeys != null) {
			for (String loggerKey : loggerKeys) {
				SaturnLogger saturnLogger = new SaturnLogger(loggerKey);
				String level = getLoggerLevelFromDatabase(loggerKey);
				if (level == null) {
					level = this.loggerDefaultLevelMap.get(loggerKey);
				}
				saturnLogger.setLevel(level);

				loggers.put(loggerKey, saturnLogger);
			}
		}
	}

	private String getLoggerLevelFromDatabase(String keyId) {
		String loggerLevel = null;
		
		Connection connection = null;
		PreparedStatement preparedStatement = null;
		ResultSet rs = null;

		try {
			connection = DatabaseManager.getInstance().getConnection();
			if (connection == null) {
				return null;
			}

			preparedStatement = connection.prepareStatement(QUERY_SQL);
			preparedStatement.setString(1, keyId);
			rs = preparedStatement.executeQuery();

			if (rs.next()) {
				loggerLevel = rs.getString("value");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (preparedStatement != null) {
					preparedStatement.close();
				}

				if (rs != null) {
					rs.close();
				}

				if (connection != null) {
					connection.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return loggerLevel;
	}
	
	private void deleteLoggerLevelFromDatabase(String keyId) {
		Connection connection = null;
		PreparedStatement preparedStatement = null;

		try {
			connection = DatabaseManager.getInstance().getConnection();
			if (connection == null) {
				return;
			}

			preparedStatement = connection.prepareStatement(DELETE_SQL);
			preparedStatement.setString(1, keyId);
			preparedStatement.executeUpdate(); //无需判断是否删除成功
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (preparedStatement != null) {
					preparedStatement.close();
				}

				if (connection != null) {
					connection.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	private void setLoggerFromDatabase(String key, String level) {
		String databaseLevel = this.getLoggerLevelFromDatabase(key);
		if (databaseLevel != null) {
			if (!databaseLevel.equals(level)) {
				this.updateLoggerLevelFromDatabase(key, level);
			}
		}else{
			this.insertLoggerLevelFromDatabase(key, level);
		}
		
	}
	
	private void updateLoggerLevelFromDatabase(String key, String level) {
		Connection connection = null;
		PreparedStatement preparedStatement = null;

		try {
			connection = DatabaseManager.getInstance().getConnection();
			if (connection == null) {
				return;
			}

			preparedStatement = connection.prepareStatement(UPDATE_SQL);
			preparedStatement.setString(1, level);
			preparedStatement.setString(2, key);
			preparedStatement.executeUpdate(); //无需判断是否删除成功
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (preparedStatement != null) {
					preparedStatement.close();
				}

				if (connection != null) {
					connection.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	private void insertLoggerLevelFromDatabase(String key, String level) {
		Connection connection = null;
		PreparedStatement preparedStatement = null;

		try {
			connection = DatabaseManager.getInstance().getConnection();
			if (connection == null) {
				return;
			}

			preparedStatement = connection.prepareStatement(INSERT_SQL);
			preparedStatement.setString(1, "logger" + key);
			preparedStatement.setString(2, "logger");
			preparedStatement.setString(3, key);
			preparedStatement.setString(4, level);
			preparedStatement.executeUpdate(); //无需判断是否删除成功
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (preparedStatement != null) {
					preparedStatement.close();
				}

				if (connection != null) {
					connection.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

}
